package websocket

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/websocket"
	"iron_man_comm/src/common/log"
	"iron_man_comm/src/goroslib"
	"net/http"
	"sync"
)

const (
	IOT_TOPIC_CMD_DOWN            = "/user/cmd/down"             //命令下发
	IOT_TOPIC_CMD_UPDATE          = "/user/cmd/update_params"    //命令更新
	IOT_TOPIC_CMD_REPLY           = "/user/cmd/reply"            //响应服务端下发的指令
	IOT_TOPIC_STATUS_UP           = "/user/status/up"            //上报机器状态
	IOT_TOPIC_TASK_DATA_UP        = "/user/task/data/up"         //任务执行产生的数据上报
	TOP_TOPIC_DATA_UP             = "/user/data/up"              //数据上报
	IOT_TOPIC_PROPERTY            = "/property/set"              //属性设置
	IOT_TOPIC_TASK_UP             = "/user/task/up"              //上报命令执行结果
	IOT_TOPIC_EXCEPTION_UP        = "/user/exception/up"         //异常上报
	IOT_TOPIC_SERVER_MAP_UP       = "/user/server/map/up"        //上报需服务端存储的地图数据
	IOT_TOPIC_MAP_DATA_UP         = "/app/map/up"                //地图数据上报
	IOT_TOPIC_PROPERTY_POST       = "/event/property/post"       //属性上报
	IOT_TOPIC_PROPERTY_POST_REPLY = "/event/property/post_reply" //属性上报响应
)

/**
 * @brief:	需要发布的topic
 */
var IOT_PUBLISHER_TOPIC = []string{
	IOT_TOPIC_CMD_DOWN,
	IOT_TOPIC_CMD_UPDATE,
	IOT_TOPIC_PROPERTY,
	IOT_TOPIC_PROPERTY_POST_REPLY,
}

/**
 * @brief:	需要订阅的topic
 */
var IOT_SUBSCRIBER_TOPIC = []string{
	IOT_TOPIC_CMD_REPLY,
	IOT_TOPIC_STATUS_UP,
	IOT_TOPIC_TASK_DATA_UP,
	TOP_TOPIC_DATA_UP,
	IOT_TOPIC_TASK_UP,
	IOT_TOPIC_EXCEPTION_UP,
	IOT_TOPIC_SERVER_MAP_UP,
	IOT_TOPIC_MAP_DATA_UP,
	IOT_TOPIC_PROPERTY_POST,
}

type IotServer struct {
	MachineHost   string
	mutex         sync.Mutex
	conn          *websocket.Conn
	node          *goroslib.Node
	publisherMap  map[string]*goroslib.Publisher
	subscriberMap map[string]*goroslib.Subscriber
}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin:     func(r *http.Request) bool { return true },
}

func (s *IotServer) StartServer(port int) error {
	s.publisherMap = make(map[string]*goroslib.Publisher)
	s.subscriberMap = make(map[string]*goroslib.Subscriber)
	http.HandleFunc("/ws", s.wsHandler)
	err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
	if nil != err {
		return err
	}
	return nil
}

func (s *IotServer) wsHandler(w http.ResponseWriter, r *http.Request) {
	hostAddress := r.RemoteAddr
	log.Info("new client connected, host: %s", hostAddress)
	// 将HTTP连接升级为WebSocket连接
	var err error
	s.conn, err = upgrader.Upgrade(w, r, nil)
	if err != nil {
		return
	}
	defer s.conn.Close()

	for {
		// 读取消息
		messageType, message, err := s.conn.ReadMessage()
		if err != nil {
			s.conn = nil
			log.Error("read msg error: %s", err.Error())
			break
		}
		if websocket.TextMessage == messageType {
			s.processWSData(string(message))
		}
	}
	log.Info("done, host: %s", hostAddress)
}

/**
 * @brief:  处理ws读取到的数据
 * @author: haitao.lin
 */
func (s *IotServer) processWSData(message string) {
	var cmd WebsocketMessage
	err := json.Unmarshal([]byte(message), &cmd)
	if err != nil {
		log.Error("iot server process ws cmd fail, msg: %s", message)
		return
	}
	switch cmd.Cmd {
	case "connect":
		s.processConnectData(message)
		break
	case "iot_cmd":
		s.processIOTData(message)
		break
	case "motor_control":
		s.processCleanMotorControlData(message)
		break
	case "device_extend":
		s.processExtendMotorControlData(message)
		break
	case "light_control":
		s.processLightControlData(message)
		break
	case "sensor_control":
		s.processSensorControlData(message)
		break
	case "mcu_poweroff":
		s.processMCUPowerOffData(message)
		break
	default:
		log.Error("get unknown cmd type, cmd: %s", cmd.Cmd)
		break
	}
}

type WebsocketMessage struct {
	Cmd string `json:"cmd"`
}

type ConnectMessage struct {
	Cmd  string             `json:"cmd"`
	Data ConnectMessageData `json:"data"`
}

type ConnectMessageData struct {
	Host string `json:"host"`
}

type SensorControlData struct {
	SensorType uint8 `json:"sensor_type"`
	OnOff      bool  `json:"on_off"`
}

type ConnectWebsocketMessage struct {
	Cmd  string               `json:"cmd"`
	Data ConnectWebsocketData `json:"data"`
}
type ConnectWebsocketData struct {
	Result bool   `json:"result"`
	Msg    string `json:"msg"`
}

type ROSStateMessage struct {
	Cmd  string              `json:"cmd"`
	Data ROSStateMessageData `json:"data"`
}

type ROSStateMessageData struct {
	State uint8  `json:"state"`
	Msg   string `json:"msg"`
}

/**
{
    "cmd": "connect_state",
    "data": {
        "state": 0, //0:连接中，1：已断开
        "msg": "xxx" //断开报错信息
    }
}
*/
func (s *IotServer) writeROSState(state uint8, msg string) {
	data := ROSStateMessage{
		Cmd: "connect_state",
		Data: ROSStateMessageData{
			State: state,
			Msg:   msg,
		},
	}
	bytes, err := json.Marshal(data)
	if err != nil {
		log.Error("ros state write data fail, json marshal fail, err: %s", err.Error())
		return
	}
	s.writeWSData(bytes)
}

/**
 * @brief:  发送ws消息
 * @author: haitao.lin
 */
func (s *IotServer) writeWSData(bytes []byte) {
	s.mutex.Lock()
	if s.conn != nil {
		err := s.conn.WriteMessage(websocket.TextMessage, bytes)
		if err != nil {
			log.Error("websocket connect write fail, err: %s, data: %s", err.Error(), string(bytes))
			s.mutex.Unlock()
			return
		}
	}
	s.mutex.Unlock()
}

/**
{
    "cmd": "iot_report",
    "data": {
     	"topic": "/user/data/up", 	//topic name
    	"data": "xxxxx"				//iot消息内容
    }
}
*/
func (s *IotServer) writeIOTData(topicName string, msg string) {
	data := IotWebsocketMessage{Cmd: "iot_report", Data: IotWebsocketData{
		Topic: topicName, Data: msg,
	}}

	bytes, err := json.Marshal(data)
	if err != nil {
		log.Error("iot server write data fail, json marshal fail, err: %s", err.Error())
		return
	}
	s.mutex.Lock()
	if s.conn != nil {
		err = s.conn.WriteMessage(websocket.TextMessage, bytes)
		if err != nil {
			log.Error("topic: %s subscriber callback, but ws write fail, err: %s", topicName, err.Error())
			s.mutex.Unlock()
			return
		}
		log.Info("topic: %s, ws write pass, data: %s", topicName, msg)
	}
	s.mutex.Unlock()
}

/**
 * @brief:  通知ws client，connect结果
 * @author: haitao.lin
 */
func (s *IotServer) writeWSConnectResult(res bool, msg string) {
	data := ConnectWebsocketMessage{
		Cmd: "connect",
		Data: ConnectWebsocketData{
			Result: res,
			Msg:    msg,
		},
	}
	bytes, err := json.Marshal(data)
	if err != nil {
		log.Error("iot server write data fail, json marshal fail, err: %s", err.Error())
		return
	}
	s.mutex.Lock()
	if s.conn != nil {
		err = s.conn.WriteMessage(websocket.TextMessage, bytes)
		if err != nil {
			log.Error("connect result write fail, err: %s", err.Error())
			s.mutex.Unlock()
			return
		}
	} else {
		log.Warn("ws no client, data skip, %s", string(bytes))
	}
	s.mutex.Unlock()
}
